package com.dream.libxrec.utils;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.Size;

import java.util.ArrayList;
import java.util.HashMap;

public class OsdHelper {

    private int mOsdLevel;
    private final int mOsdPadding;
    private Typeface mTypeface = null;
    private final int mOsdHeight;

    /**
     * 水印大小,一般情况，如1280x720的YUV，那么等级为4是比较合适
     * @param level 水印大小
     * @param padding 文字之间的间隔
     */
    public OsdHelper(int level, int padding) {
        if (level < 1){
            level = 1;
        }

        this.mOsdPadding = padding;
        this.mOsdLevel = level;
        this.mOsdHeight = mOsdLevel*8;
    }

    /**
     * 设置字体，可以不调用
     * @param typeface
     */
    public void loadFont(Typeface typeface){
        this.mTypeface = typeface;
    }

    /**
     * 根据画面的分辨率设置水印大小
     * @param s YUV数据分辨率
     */
    public synchronized void setLevelByImageSize(Size s){
        int max = Math.max(s.getHeight(),s.getWidth());
        int level;
        if (max >= 3840){
            level = 10;
        }else if (max >= 1920){
            level = 6;
        } else if (max >= 1280) {
            level = 5;
        } else if (max >= 800){
            level = 4;
        } else{
            level = 3;
        }
        setOsdLevel(level);
    }

    public synchronized void setOsdLevel(int level){
        if (mOsdLevel != level){
            this.mOsdLevel = level;
            mOSDMap.clear();
        }
    }

    private static final int CHAR_WIDTH = 8;//默认一维点阵的宽度是8，等级比较大的文字由多个点阵合成

    //用二维数组是为了处理汉字，如果还是8*8,那显示汉字会比较勉强，动态生成点阵需要采用16*16或更高
    private final HashMap<Character,OsdInfo> mOSDMap = new HashMap<>();

    /**
     *
     * @param yuvData 图像数据，YUV420采样格式均可(只用到了Y分量)，如NV21,NV12,I420(YU12),YV12等
     * @param frameWidth 图像宽度
     * @param frameHeight 图像高度
     * @param osdStr 水印内容
     * @param startX 水印离图像左边的偏移量，如果是负数，那么从右往左绘制
     * @param startY 水印离图像上边的偏移量 如果是负数，那么从下往上绘制
     */
    public synchronized void addYuvOsdText(byte[] yuvData, int frameWidth,
                               int frameHeight, String osdStr,int startX, int startY){

        final byte MAX_COLOR = (byte) 0xFF;//最大亮度

        ArrayList<OsdInfo> list = new ArrayList<>();

        for (Character s:osdStr.toCharArray()){
            OsdInfo info = mOSDMap.get(s);
            if (info != null){
                list.add(info);
            }else{
                OsdInfo i = convertCharacterToByteArray(s,mOsdLevel);
                mOSDMap.put(s,i);
                list.add(i);
            }
        }

        int offset = 0;//已绘制的点阵数量

        if (startX < 0){
            int w = 0;
            for (OsdInfo info:list){
                w += info.width+mOsdPadding;
            }

            startX = frameWidth - w + startX;
        }

        if (startY < 0){
            startY = frameHeight - mOsdHeight + startY;
        }

        for (int i =0;i<list.size();i++) {//遍历每一个字符，然后将字符对应的点阵绘制到YUV上
            OsdInfo info = list.get(i);

            int height = info.data[0].length;
            int num = info.data.length;//点阵数量

            for (int n = 0;n< num;n++){
                for (int j = 0, y = startY; j < height && y < frameHeight-1; j++, y++) {//遍历获取点阵的高
                    byte p8 = info.data[n][j];
                    int mask8 = 0x80&0xFF;

                    for (int k = 0, x = startX + offset * CHAR_WIDTH; k < CHAR_WIDTH && x < frameWidth-1; k++, x++) {//遍历点阵的宽，每个字节有8位，宽为8
                        if ((p8 & mask8) != 0) {
                            //我们只需要修改YUV中的Y数据（即亮度）,Y数据在 [0 ~ width*height]这个区间里。
                            yuvData[y * frameWidth +x] = MAX_COLOR;//如果点阵为1，那么就将对应的yuv图像绘制水印的地方的像素的亮度设置为最大亮度，观感上为白色
                        }
                        mask8 >>= 1; // Shift right to check the next bit.
                    }
                }
                offset++;
            }

            startX -= (num*8-info.width-mOsdPadding);
            //向右偏移，减去无效字节占用的长度（减去后文字间就没有间隙了）并加上指定的文字间隙
        }
    }

    public void addYuvOsdRect(byte[] yuvData, int frameWidth, int frameHeight, Rect rect){
        addYuvOsdRect(yuvData,frameWidth,frameHeight,rect.left,rect.top,rect.right,rect.bottom);
    }

    public void addYuvOsdRect(byte[] yuvData,final int frameWidth, final int frameHeight,final int left,final int top, final int right,final int bottom){
        final byte MAX_COLOR = (byte) 0xFF;//最大亮度

        //绘制横线
        for (int i = left;i < right && i < frameWidth-1; i++) {
            yuvData[top * frameWidth + i] = MAX_COLOR;
            yuvData[bottom * frameWidth + i] = MAX_COLOR;
        }

        //绘制竖线
        for (int i = top;i < bottom && i < frameHeight-1; i++) {
            yuvData[i * frameWidth + left] = MAX_COLOR;
            yuvData[i * frameWidth + right] = MAX_COLOR;
        }
    }

    /**
     * 动态生成点阵
     * @param ch 单个字符
     * @return 点阵
     */
    private OsdInfo convertCharacterToByteArray(final char ch, final int level) {
        OsdInfo info = new OsdInfo();
        info.ch = ch;

        //处理空格的长度
        if (ch == ' '){
            info.width = 8;
            info.data = new byte[1][8];
            return info;
        }

        int osdLen = 8*level;//文字点阵一般为正方形，边长

        Bitmap bitmap = Bitmap.createBitmap(osdLen, osdLen, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);

        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        if (mTypeface != null){
            paint.setTypeface(mTypeface);
        }
        //抗锯齿
        paint.setAntiAlias(true);

        paint.setTextSize(osdLen);

        byte[][] byteArray = new byte[level][osdLen];
        canvas.drawText(String.valueOf(ch),0,bitmap.getHeight()-2, paint);

        int width = 0;
        for (int y = 0; y < osdLen; y++) {
            int byteValue = 0;
            for (int block = 0; block < byteArray.length; block++) {
                int startX = block * 8;
                int endX = startX + 8;
                for (int x = startX; x < endX; x++) {
                    int pixelColor = bitmap.getPixel(x, y);
                    if (pixelColor!= Color.TRANSPARENT) {
                        byteValue |= (1 << (7 - (x - startX)));
                        if (x > width){
                            width = x;
                        }
                    }
                }
                byteArray[block][y] = (byte) byteValue;
                byteValue = 0;
            }
        }

        info.width = width;

        //由于视频水印是重复次数极多的场景，尽量去减少数据量
        int chunks = width / 8;
        if (width%8 != 0){
            chunks += 1;
        }
        info.data = new byte[chunks][];
        System.arraycopy(byteArray, 0, info.data, 0, chunks);

        bitmap.recycle();
        return info;
    }

    /**
     * 获取水印文字的高度
     * @return
     */
    public int getOsdHeight(){
        return mOsdHeight;
    }

    public void clean(){
        //清空新增的水印
        mOSDMap.clear();
    }

    private static class OsdInfo{
        public char ch;
        public byte[][] data;
        public int width;
    }
}